/*
 * idt.c
 * 
 * Interrupt Descriptor Table
 * 
 */

#include <idt.h>
#include <i386.h>
#include <pic.h>
#include <gdt.h>
#include <isr.h> 

extern void isr_set_handlers();

/** Estructuras **/

/* Interrupt Descriptor Table */

uint_64 idt_table[IDT_LENGTH] __attribute__((aligned(8))) = { IDT_NULL };

/* Interrupt Descriptor Table Register */

idt_reg idtr = { sizeof(idt_table) - 1, (uint_32) &idt_table };

/** Funciones **/

/** 
 * idt_init - Inicializa las estructuras de la IDT
 * 
 */
 
void idt_init()
{
    /* Llamo a una funcion en assembler que registra en la IDT los 
     * handlers de atencion de excepciones e interrupciones llamando a
     * idt_register(). Estos son atendidos por un handler generico */
     
    isr_set_handlers();
    
    /* Reemplazo el handler generico de algunas interrupciones */
    
    isr_init();

    /* Cargo el registro IDTR */

    lidt(idtr);

    /* Reseteo y habilito el PIC */

    pic_reset();
    pic_enable();
}

/**
 * idt_register - Funcion que registra el nuevo handler @handler (registrado como Interrupt Gate) para la interrupcion @index
 *
 * Si @index no es un valor valido, la funcion devuelve IDT_BAD_INDEX
 * Si @index ya tiene un handler de interrupcion asociado, la funcion devuelve IDT_INDEX_IN_USE
 * En caso de exito, la funcion devuelve IDT_OK
 *
 */
 
uint_32 idt_register(uint_32 indice, void *handler)
{
	/* Veo que el indice sea valido */
    if((indice < 0) || (indice > IDT_LENGTH - 1))
		return IDT_BAD_INDEX;

    /* Veo que la interrupcion no este siendo atendida */
    if(idt_table[indice] & IDT_DESC_P)
		return IDT_INDEX_IN_USE;

	/* Si se quiere registrar el handler de la syscall, el nivel de privilegio es 3 */
    uint_64 dpl = (indice == IDT_INDEX_SYSCALL) ? IDT_DESC_DPL(0x3) : IDT_DESC_DPL(0x0);

    /* Completamos el descriptor en la IDT */
    idt_table[indice] = IDT_DESC_SEGSEL(GDT_INDEX_KERNEL_C << 3 | 0x0) | IDT_DESC_OFFSET(handler) | IDT_DESC_P | IDT_DESC_TYPE(IDT_TYPE_S_32BIG) | dpl;
    
    return IDT_OK;	
}
